package com.xiaohe66.web.infrastructure.sec;

import com.xiaohe66.common.util.HeJwt;
import com.xiaohe66.common.util.IdUtils;
import com.xiaohe66.common.web.sec.AbstractSecurityService;
import com.xiaohe66.common.web.util.WebUtils;
import com.xiaohe66.web.domain.account.value.AccountId;
import com.xiaohe66.web.domain.account.value.AccountName;
import com.xiaohe66.web.domain.sys.sec.entity.CurrentAccount;
import com.xiaohe66.web.domain.sys.sec.repository.SysConfigRepository;
import com.xiaohe66.web.domain.sys.sec.service.SecurityService;
import com.xiaohe66.web.domain.sys.sec.value.RoleName;
import com.xiaohe66.web.domain.sys.sec.value.SysJwtSecret;
import com.xiaohe66.web.integration.domain.StringValue;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author xiaohe
 * @since 2023.07.11 16:42
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SecurityServiceImpl extends AbstractSecurityService implements SecurityService, ApplicationRunner {

    private static final String CURRENT_ACCOUNT_KEY = "currentAccount";

    private final SysConfigRepository sysConfigRepository;

    private HeJwt<TokenAccount> heJwtInstance;

    @Override
    public void login(CurrentAccount currentAccount) {
        // none
    }

    @Override
    public void setCurrentAccount(CurrentAccount account) {
        setAttribute(CURRENT_ACCOUNT_KEY, account);
    }

    @Override
    public void logout() {
        WebUtils.getSession().removeAttribute(CURRENT_ACCOUNT_KEY);
    }

    @Override
    public boolean isLogin() {
        CurrentAccount currentAccount = getAttribute(CURRENT_ACCOUNT_KEY);
        return currentAccount != null;
    }

    @Override
    public String getSessionId() {
        return WebUtils.getSession().getId();
    }

    @Override
    public CurrentAccount getCurrentAccount() {
        return getAttribute(CURRENT_ACCOUNT_KEY);
    }

    @Override
    public AccountId getCurrentAccountId() {
        CurrentAccount currentAccount = getCurrentAccount();
        return currentAccount == null ? null : currentAccount.getId();
    }

    @Override
    public void setAttribute(Object key, Object value) {
        HttpSession session = WebUtils.getSession();
        if (key instanceof String k) {
            session.setAttribute(k, value);
        } else {
            session.setAttribute(key.toString(), value);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public <V> V getAttribute(Object key) {
        HttpSession session = WebUtils.getSession();
        if (key instanceof String k) {
            return (V) session.getAttribute(k);
        } else {
            return (V) session.getAttribute(key.toString());
        }
    }

    @Override
    public boolean hasCurrentAccountPermission(Long aLong) {
        if (isAdmin()) {
            return true;
        }
        AccountId accountId = getCurrentAccountId();
        return accountId != null && aLong.equals(accountId.getValue());
    }

    @Override
    public boolean hasCurrentAccountPermission(AccountId createId) {
        if (isAdmin()) {
            return true;
        }
        return createId.equals(getCurrentAccountId());
    }

    @Override
    public String createToken(CurrentAccount currentAccount) {

        List<String> roleNames = currentAccount.getRoleNames().stream()
                .map(StringValue::getValue)
                .toList();

        TokenAccount tokenAccount = new TokenAccount();
        tokenAccount.setId(currentAccount.getId().getValue());
        tokenAccount.setName(currentAccount.getName().getValue());
        tokenAccount.setRoleNames(roleNames);

        return heJwtInstance.genToken(tokenAccount);
    }

    @Override
    public CurrentAccount verifyTokenAndGet(String token) {

        TokenAccount tokenAccount = heJwtInstance.verifyAndGet(token);

        Set<RoleName> roleNames = tokenAccount.getRoleNames().stream()
                .map(RoleName::new)
                .collect(Collectors.toSet());

        // TODO : need permissionNames

        return CurrentAccount.builder()
                .id(new AccountId(tokenAccount.getId()))
                .name(new AccountName(tokenAccount.getName()))
                .roleNames(roleNames)
                .permissionNames(new HashSet<>())
                .build();
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {

        log.info("load system jwtSecret start");

        SysJwtSecret jwtSecret = sysConfigRepository.getSysJwtSecret();

        if (jwtSecret == null) {
            // 初始化
            jwtSecret = new SysJwtSecret(IdUtils.uuidSimple());
            sysConfigRepository.saveJwtSecret(jwtSecret);
        }

        this.heJwtInstance = new HeJwt<>(60 * 24, "xiaohe66-web", jwtSecret.getValue(), TokenAccount.class);

        log.info("load system jwtSecret end");
    }

    @Data
    public static class TokenAccount {

        private Long id;
        private String name;
        private Collection<String> roleNames;

    }
}
